﻿using System;
using System.Data;
using System.Linq;
using System.Reflection;
using System.Globalization;
using System.Collections.Generic;
using NazTek.Utility.Extensions.SimpleType;

namespace NazTek.Utility.Extensions.Generic
{
    /// <summary> 
    /// Extends generic IEnumerable&lt;T&gt;
    /// </summary> 
    /// <remarks></remarks> 
    public static class GenericEnumerableExtension
    {
        /// <summary> 
        /// Extract data from the supplied list and builds a DataSet with publicly readable non-indexed value types as columns, using reflection 
        /// </summary> 
        /// <typeparam name="T">Type of the composite items to extract data from</typeparam> 
        /// <param name="genericEnumerable">The IEnumerable list to convert to DataSet</param> 
        /// <returns>Hydrated DataSet</returns> 
        /// <remarks></remarks> 
        /// <example>
        /// <code>
        /// // context
        /// internal class Person
        /// {
        ///     internal string Name { get; set; }
        ///     internal Person(string pName)
        ///     {
        ///         this.Name = pName;
        ///     }
        /// }
        /// List&lt;Person&gt; MyList = new List&lt;Person&gt;();
        /// MyList.Add(new Person("Bob"));
        /// MyList.Add(new Person("Jane"));
        /// 
        /// // usage
        /// DataSet ds = MyList.GetDataSet&lt;Person&gt;();
        /// </code>
        /// </example>
        public static DataSet GetDataSet<T>(this IEnumerable<T> genericEnumerable) where T : class
        {
            DataSet ds = new DataSet("DataSet");
            ds.Locale = CultureInfo.InvariantCulture;
            ds.Tables.Add(genericEnumerable.GetDataTable<T>());
            return ds;
        }
        /// <summary> 
        /// Extract data from the supplied list and builds a DataTable with publicly readable non-indexed value types as columns, using reflection 
        /// </summary> 
        /// <typeparam name="T">Type of the composite items to extract data from</typeparam> 
        /// <param name="genericEnumerable">The IEnumerable list to convert to </param> 
        /// <returns>Hydrated DataTable</returns> 
        /// <remarks></remarks> 
        /// <example>
        /// <code>
        /// // context
        /// internal class Person
        /// {
        ///     internal string Name { get; set; }
        ///     internal Person(string pName)
        ///     {
        ///         this.Name = pName;
        ///     }
        /// }
        /// List&lt;Person&gt; MyList = new List&lt;Person&gt;();
        /// MyList.Add(new Person("Bob"));
        /// MyList.Add(new Person("Jane"));
        /// 
        /// // usage
        /// DataTable dt = MyList.GetDataTable&lt;Person&gt;();
        /// </code>
        /// </example>
        public static DataTable GetDataTable<T>(this IEnumerable<T> genericEnumerable) where T : class
        {
            if (genericEnumerable == null || genericEnumerable.Count<T>() == 0)
                return null;

            DataTable dt = new DataTable(typeof(T).Name);
            dt.Locale = CultureInfo.InvariantCulture;
            T oSampleEntity = genericEnumerable.First<T>();
            foreach (PropertyInfo prop in oSampleEntity.GetType().GetProperties(BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
            {
                {
                    if (prop.CanRead && prop.GetIndexParameters().Length == 0 && (prop.PropertyType.IsValueType || object.ReferenceEquals(prop.PropertyType, typeof(string))))
                    {
                        Type oType = prop.PropertyType;
                        if (oType.IsGenericType)
                        {
                            oType = oType.GetGenericArguments()[0];
                        }
                        dt.Columns.Add(prop.Name, oType);
                    }
                }
            }
            foreach (T item in genericEnumerable)
            {
                List<object> oValueList = new List<object>();
                foreach (DataColumn dc in dt.Columns)
                {
                    oValueList.Add(item.GetType().GetProperty(dc.ColumnName, BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetValue(item, null));
                }
                dt.Rows.Add(oValueList.ToArray());
            }
            return dt;
        }
        /// <summary> 
        /// Concatenates the values of an IEnumerable list into a string 
        /// </summary> 
        /// <typeparam name="T">Enumerated type</typeparam> 
        /// <param name="genericEnumerable">The list to convert to string</param> 
        /// <param name="itemSeparator">Seperate the items with</param> 
        /// <param name="compact">Remove null/empty items</param> 
        /// <returns>Concatenated items string</returns> 
        /// <remarks>Each item is converted to string via object.ToString(), which must be overridden for reference types for accurate results</remarks> 
        /// <example>
        /// <code>
        /// // context
        /// IEnumerable&lt;string&gt; stringArray = new string[] { "one", null, "two", "", "three", " " };
        /// 
        /// // usage
        /// string s = stringArray.ToString&lt;string&gt;(",", true)); // "one,two,three"
        /// </code>
        /// </example>
        public static string ToString<T>(this IEnumerable<T> genericEnumerable, string itemSeparator, bool compact)
        {
            return GenericArrayExtension.ToString<T>(genericEnumerable.ToArray(), itemSeparator, compact);
        }
        /// <summary> 
        /// Concatenate the values of an IEnumerable list into a string 
        /// </summary> 
        /// <typeparam name="T">Enumerated type</typeparam> 
        /// <param name="genericEnumerable">The list to convert to string</param> 
        /// <param name="itemSeparator">Seperates the items with</param> 
        /// <returns>Concatenated items string</returns> 
        /// <remarks>Each item is converted to string via object.ToString(), which must be overridden for reference types for accurate results</remarks> 
        /// <example>
        /// <code>
        /// // context
        /// IEnumerable&lt;string&gt; stringArray = new string[] { "one", null, "two", "", "three", " " };
        /// 
        /// // usage
        /// string s = stringArray.ToString&lt;string&gt;(",")); // "one,,two,,three"
        /// </code>
        /// </example>
        public static string ToString<T>(this IEnumerable<T> genericEnumerable, string itemSeparator)
        {
            return genericEnumerable.ToString<T>(itemSeparator, false);
        }
    }
}
